package org.framework.lazy.cloud.network.heartbeat.protocol.handler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.socksx.v5.Socks5AddressType;
import io.netty.handler.codec.socksx.v5.Socks5CommandRequest;
import io.netty.handler.codec.socksx.v5.Socks5CommandType;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettyProxyMsg;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettySocketChannelContext;
import org.framework.lazy.cloud.network.heartbeat.common.constant.ProxyMessageType;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import org.framework.lazy.cloud.network.heartbeat.protocol.route.ClientProxyRoute;
import org.framework.lazy.cloud.network.heartbeat.protocol.route.ProxyRoute;
import org.framework.lazy.cloud.network.heartbeat.protocol.route.RouteContext;
import org.framework.lazy.cloud.network.heartbeat.protocol.route.RouteType;
import org.wu.framework.core.NormalUsedString;
import org.wu.framework.core.utils.ObjectUtils;

import java.util.UUID;

@Slf4j
public class NettySocks5CommandRequestHandler extends SimpleChannelInboundHandler<Socks5CommandRequest> {
    private final ChannelTypeAdapter channelTypeAdapter;

    public NettySocks5CommandRequestHandler(ChannelTypeAdapter channelTypeAdapter) {
        this.channelTypeAdapter = channelTypeAdapter;
    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        if (channel instanceof NioSocketChannel) {
            log.info("socks 这是一个TCP通道");
        } else if (channel instanceof NioDatagramChannel) {
            log.info("socks 这是一个UDP通道");
        } else {
            log.info("socks 未知类型的通道");
        }
        String visitorId = UUID.randomUUID().toString();
        ChannelAttributeKeyUtils.buildVisitorId(channel, visitorId);
        super.channelActive(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest request) throws Exception {
        if (request.type() == Socks5CommandType.CONNECT) {

            String originHost = request.dstAddr();
            int originPort = request.dstPort();
            Socks5AddressType socks5AddressType = request.dstAddrType();
            String visitorId = ChannelAttributeKeyUtils.getVisitorId(ctx.channel());

            ChannelAttributeKeyUtils.buildTargetIp(ctx.channel(),originHost);
            ChannelAttributeKeyUtils.buildTargetPort(ctx.channel(),originPort);
            NettyProxyMsg proxyMsg = new NettyProxyMsg();

            proxyMsg.setVisitorId(visitorId);
            ProxyRoute route = RouteContext.getRoute(originHost, String.valueOf(originPort));


            if (ObjectUtils.isEmpty(route)) {
                // 未查询到路由信息、本地代理
                proxyMsg.setType(ProxyMessageType.SOCKS_LOCAL_PROXY);
                proxyMsg.setTargetIp(originHost);
                proxyMsg.setTargetPort(originPort);
            } else {
                // 其他路由代理
                String targetIp = route.getTargetIp();
                String targetPort = NormalUsedString.ASTERISK.equals(route.getTargetPort()) ? String.valueOf(originPort) : route.getTargetPort();
                proxyMsg.setTargetIp(targetIp);
                proxyMsg.setTargetPort(Integer.parseInt(targetPort));
                if (RouteType.LOCAL.equals(route.getRouteType())) {
                    proxyMsg.setType(ProxyMessageType.SOCKS_LOCAL_PROXY);
                } else if (RouteType.CLIENT_PROXY_CLIENT.equals(route.getRouteType())) {
                    ClientProxyRoute clientProxyRoute = (ClientProxyRoute) route;
                    String clientId = clientProxyRoute.getTargetClientId();
                    proxyMsg.setClientId(clientId);
                    proxyMsg.setType(ProxyMessageType.SOCKS_CLIENT_PROXY_CLIENT_);
                } else if (RouteType.CLIENT_PROXY_SEVER.equals(route.getRouteType())) {
                    proxyMsg.setType(ProxyMessageType.SOCKS_CLIENT_PROXY_SERVER_);
                } else if (RouteType.SERVER_PROXY_CLIENT.equals(route.getRouteType())) {
                    ClientProxyRoute clientProxyRoute = (ClientProxyRoute) route;
                    String clientId = clientProxyRoute.getTargetClientId();
                    proxyMsg.setClientId(clientId);
                    proxyMsg.setType(ProxyMessageType.SOCKS_SERVER_PROXY_CLIENT_);
                }
            }

            // 绑定当前通道地址类型
            ChannelAttributeKeyUtils.buildSocks5AddressType(ctx.channel(), socks5AddressType.byteValue());
            NettySocketChannelContext nettyChannelContext = new NettySocketChannelContext();
            nettyChannelContext.setChannelHandlerContext(ctx);
            nettyChannelContext.setSocks5AddressType(request.dstAddrType());
            channelTypeAdapter.handler(nettyChannelContext, proxyMsg);

        } else {
            log.info("receive commandRequest type={}", request.type());
            ReferenceCountUtil.retain(request);
            ctx.fireChannelRead(request);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}    